多重界面

  应该清楚,我们为Parser演化出来的名字空间定义并不是Parser提供给它的用户的界面。相反,它是为能方便地写出各个函数所需要的一组声明。Parser提供给它的用户的界面远比这简单得多:

    namespace Parser {
        double expr(bool);
    }

幸运的是,Parser的这两个名字空间定义可以共存,这就使各种定义可以被用到最合适的地方。我们看到名字空间Parser被用于提供两种东西:

1)、实现分析器的所有函数的一个公共环境。
2)、分析器提供给它的用户的一个外部界面。

这样,驱动程序main()就应该只看到:

    namespace Parser {            // 给用户的界面
        double expr(bool);
    }

而实现Parser的那些函数则应该看到我们前面所确定的,对表述这些函数的共享环境而言最合适的界面,即

    namespace Parser {            // 给实现的界面
        double prim(bool);
        double term(bool);
        double expr(bool);

        using Lexer::get_token;    // 使用Lexer的get_token
        using Lexer::curr_tok;     // 使用Lexer的curr_tok
        using Error::error;        // 使用Error的error
    }

为实现所提供的界面比为用户提供的界面更大一些。如果这个界面真对的是现实程序里的一个现实规模的模块,那么它通常也会比用户能够看到的界面变化得更频繁些。将模块的用户(在这里是main()使用Parser)与这些变化隔离开是非常重要的。

  我们并不需要两个互相独立的名字空间来描述这两个不同的界面。如果真需要的话我们也能做得到。设计界面是最基本的设计活动之一,而且是一种可以获得或者丧失重要利益的活动。因此,我们很值得去考虑到底想达到什么结果,并讨论若干不同的选择。

  请记住,这里所介绍的解决方案是我们考虑过的各种解中最简单的,通常也是最好的。它的主要弱点是两个界面没有采用不同的名字,编译器不一定有足够的信息去检查两个名字空间定义的一致性。当然,即使编译器未必总有机会去检查这种一致性,它通常还是会这样做。进一步说,连接系统将能捕捉到编译器遗漏的大部分错误。

  这里给出的解也是我将用于讨论物理模块化(9.3节)的解,也是我想推荐的没有进一步逻辑约束的解(8.2.7节)。

🔚